package ar.com.blitzsoftware.footgolfer;



public class Misc
{
	// http://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates
	
	
	
	private static final double R = 6371; // km
	//private static final double R = 6367; // km
	//private static final double R = 6378.1370; // km
	
//	static Boolean inCircle(double center_x, double center_y, double radius, double x, double y)
//	{
//		//square_dist = (center_x - x) ** 2 + (center_y - y) ** 2
//		//	return square_dist <= radius ** 2
//	
//		double squareDist = Math.pow(center_x - x, 2) + Math.pow(center_y - y, 2);
//		return (squareDist <= radius * radius);
//	}
	
	public static Boolean inCircle(double lat1, double lon1, double lat2, double lon2, double radius)
	{
		double distance = distVincenty(lat1, lon1, lat2, lon2);
		return (distance <= radius * radius);
	}	
	
	
	
	
	public static double distHaversine(double lat1, double lon1, double lat2, double lon2)
	{
		//double R = 6371; // km
		double x = (lon2-lon1) * Math.cos((lat1+lat2)/2);
		double y = (lat2-lat1);
		return Math.sqrt(x*x + y*y) * R;		
	}	
	
	
	//rad = function(x) {return x*Math.PI/180;}
	public static double distHaversine_2(double lat1, double lon1, double lat2, double lon2)
	{
		  //double R = 6371; // earth's mean radius in km
		  double dLat  = Math.toRadians(lat2 - lat1);
		  double dLong = Math.toRadians(lon2 - lon1);

		  double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
		             Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLong/2) * Math.sin(dLong/2);
		  
		  double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		  double d = R * c;
		  
		  return d; //d.toFixed(3);
		
	}		
	
	

	
	
	
//	public static double distOther(double lat1, double lon1, double lat2, double lon2)
//	{
//		//square_dist = (center_x - x) ** 2 + (center_y - y) ** 2
//		//	return square_dist <= radius ** 2
//	
////		var x = (lon2-lon1) * Math.cos((lat1+lat2)/2);
////		var y = (lat2-lat1);
////		var d = Math.sqrt(x*x + y*y) * R;		
//		
//		double squareDist = Math.pow(center_x - x, 2) + Math.pow(center_y - y, 2);
//		return (squareDist <= radius * radius);
//	}		
	
	

	
	public static double distVincenty(double lat1, double lon1, double lat2, double lon2) 
	{
		//http://www.movable-type.co.uk/scripts/latlong-vincenty.html
			//WGS-84	a = 6 378 137 m (±2 m)	b ≈ 6 356 752.314245 m	f ≈ 1 / 298.257223563
			//GRS-80	a = 6 378 137 m	b ≈ 6 356 752.314140 m	f = 1 / 298.257222101
			//Airy 1830	a = 6 377 563.396 m	b = 6 356 256.910 m	f ≈ 1 / 299.3249646
			//Internat’l 1924	a = 6 378 388 m	b ≈ 6 356 911.946 m	f = 1 / 297
			//Clarke mod.1880	a = 6 378 249.145 m	b ≈ 6 356 514.86955 m	f = 1 / 293.465
			//GRS-67	a = 6 378 160 m	b ≈ 6 356 774.719 m	f = 1 / 298.247167
		
		double a = 6378137, b = 6356752.314245,  f = 1/298.257223563;  // WGS-84 ellipsoid params
		double L = Math.toRadians(lon2-lon1);
		double U1 = Math.atan((1-f) * Math.tan(Math.toRadians(lat1)));
		  
		double U2 = Math.atan((1-f) * Math.tan( Math.toRadians(lat2) ));
		double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
		double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
		  
		double lambda = L, lambdaP, iterLimit = 100;
		
		
		
		double sinSigma;
		double cosSigma;
		double sigma;
		double cosSqAlpha;
		Double cos2SigmaM;
		do 
		{
			double sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
			sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
		    
			if (sinSigma==0)
			{
				return 0;
			}  // co-incident points
		    
			cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
			sigma = Math.atan2(sinSigma, cosSigma);
			double sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
			cosSqAlpha = 1 - sinAlpha*sinAlpha;
			cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
			
			if ( cos2SigmaM.isNaN() )
			{
				cos2SigmaM = 0d;
			}  // equatorial line: cosSqAlpha=0 (§6)
			
			double C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
			lambdaP = lambda;
			lambda = L + (1-C) * f * sinAlpha * (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
		} while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0);

		
		
		if (iterLimit==0)
		{
			return Double.NaN;
		}  // formula failed to converge

		double uSq = cosSqAlpha * (a*a - b*b) / (b*b);
		double A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
		double B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
		double deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM) - B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
		double s = b*A*(sigma-deltaSigma);
		  
		//s = s.toFixed(3); // round to 1mm precision
		return s;
		  
//		// note: to return initial/final bearings in addition to distance, use something like:
//		double fwdAz = Math.atan2(cosU2*sinLambda,  cosU1*sinU2-sinU1*cosU2*cosLambda);
//		double revAz = Math.atan2(cosU1*sinLambda, -sinU1*cosU2+cosU1*sinU2*cosLambda);
//		
//		return { distance: s, initialBearing: fwdAz.toDeg(), finalBearing: revAz.toDeg() };
	}	
	
	
	
//    private double GetDirection(double lat1, double lon1, double lat2, double lon2)
//    {
//    	
//    	
//        //code for Direction in Degrees
//        double dlat = Math.toRadians(lat1) - Math.toRadians(lat2);
//        double dlon = Math.toRadians(lon1) - Math.toRadians(lon2);
//        double y = Math.sin(dlon) * Math.cos(lat2);
//        double x = Math.cos(Math.toRadians(lat1)) * Math.Sin(Math.toRadians(lat2)) - Math.sin(Math.toRadians(lat1)) * Math.Cos(Math.toRadians(lat2)) * Math.Cos(dlon);
//        double direct = Math.round(Math.toDegrees(Math.atan2(y, x)), 0);
//        
//        
//        
//        if (direct < 0)
//		{
//			direct = direct + 360;
//		}
//        return (direct);
//    }	
}
